package utils

import (
	"archive/zip"
	"bytes"
	"compress/gzip"
	"compress/lzw"
	"fmt"
	"github.com/xi2/xz"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func DecompressZipToCurrentDir(zipFile string) (string, error) {
	dst := GetUnzipName(zipFile)
	err := UniversalDecompress(zipFile, dst)
	return dst, err
}

// UniversalDecompress 解压函数，支持 gz, zip 和 .Z 文件
func UniversalDecompress(src, dest string) error {
	fileType, err := getFileType(src)
	if err != nil {
		return err
	}

	switch fileType {
	case "zip":
		return unzip(src, dest)
	case "gz":
		return unGzip(src, dest)
	case "Z (compress)":
		return uncompress(src, dest)
	case "xz":
		return uncompressWithXz(src, dest)
	default:
		// 尝试通过文件扩展名判断
		ext := strings.ToLower(filepath.Ext(src))
		switch ext {
		case ".zip":
			return unzip(src, dest)
		case ".gz":
			return unGzip(src, dest)
		case ".z":
			return uncompress(src, dest)
		case ".xz":
			return uncompressWithXz(src, dest)
		default:
			return fmt.Errorf("无法识别的文件格式: %s", fileType)
		}
	}
}

// 获取输出文件名
func GetUnzipName(inputName string) string {
	return strings.TrimSuffix(inputName, filepath.Ext(inputName))
}

// unzip 解压 zip 文件
func unzip(src, dest string) error {
	r, err := zip.OpenReader(src)
	if err != nil {
		return err
	}
	defer r.Close()

	os.MkdirAll(dest, 0755)

	// 遍历 zip 文件中的每个文件/目录
	for _, f := range r.File {
		// 构建目标文件路径
		fpath := filepath.Join(dest, f.Name)

		// 检查是否为目录
		if f.FileInfo().IsDir() {
			os.MkdirAll(fpath, f.Mode())
			continue
		}

		// 创建目录
		if err = os.MkdirAll(filepath.Dir(fpath), 0755); err != nil {
			return err
		}

		// 打开 zip 中的文件
		rc, err := f.Open()
		if err != nil {
			return err
		}
		defer rc.Close()

		// 创建目标文件
		outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
		if err != nil {
			return err
		}
		defer outFile.Close()

		// 复制文件内容
		_, err = io.Copy(outFile, rc)
		if err != nil {
			return err
		}
	}

	return nil
}

// unGzip 解压 gz 文件
func unGzip(src, dest string) error {
	gzFile, err := os.Open(src)
	if err != nil {
		return err
	}
	defer gzFile.Close()

	gzReader, err := gzip.NewReader(gzFile)
	if err != nil {
		return err
	}
	defer gzReader.Close()

	// 如果目标路径是目录，则使用原始文件名
	if fi, err := os.Stat(dest); err == nil && fi.IsDir() {
		dest = filepath.Join(dest, strings.TrimSuffix(filepath.Base(src), ".gz"))
	}

	outFile, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer outFile.Close()

	_, err = io.Copy(outFile, gzReader)
	return err
}

func uncompress(src, dest string) error {

	err := uncompressWithNative(src, dest)
	if err != nil {
		if hasCommand("gzip") {
			return uncompressWithGzip(src, dest)
		}
	}
	return err
}

func uncompressWithNative(src, dest string) error {

	zFile, err := os.Open(src)
	if err != nil {
		return err
	}
	defer zFile.Close()

	// 如果目标路径是目录，则使用原始文件名
	if fi, err := os.Stat(dest); err == nil && fi.IsDir() {
		dest = filepath.Join(dest, strings.TrimSuffix(filepath.Base(src), ".Z"))
	}

	outFile, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer outFile.Close()

	orders := []lzw.Order{lzw.MSB, lzw.LSB}           // 尝试两种字节序
	widths := []int{8, 9, 10, 11, 12, 13, 14, 15, 16} //

	// 尝试不同的 LZW 参数组合
	var lastErr error
	for _, order := range orders {
		for _, width := range widths {
			zFile.Seek(0, 0) // 重置文件指针
			lzwReader := lzw.NewReader(zFile, order, width)

			_, err := io.Copy(outFile, lzwReader)
			if err == nil {
				lzwReader.Close()
				return nil // 成功解压
			}

			lastErr = err
			lzwReader.Close()
		}
	}

	return fmt.Errorf("failed to decompress .Z file, last error: %v", lastErr)
}

func hasCommand(name string) bool {
	_, err := exec.LookPath(name)
	return err == nil
}

func uncompressWithGzip(src, dest string) error {
	// 方法1：使用Output()自动处理输出
	cmd := exec.Command("gzip", "-dc", src)
	output, err := cmd.Output() // Output()已经处理了Stdout
	if err != nil {
		return fmt.Errorf("gzip cmd %v fail: %v", cmd, err)
	}
	return os.WriteFile(dest, output, 0644)

	// 方法2：直接重定向到文件
	/*
	   outFile, err := os.Create(dest)
	   if err != nil {
	       return err
	   }
	   defer outFile.Close()

	   cmd := exec.Command("gzip", "-dck", src)
	   cmd.Stdout = outFile // 直接输出到文件
	   return cmd.Run()
	*/
}

//
//func uncompressWithDSNet(src, dest string) error {
//	r, err := os.Open(src)
//	if err != nil {
//		return err
//	}
//	defer r.Close()
//
//	w, err := os.Create(dest)
//	if err != nil {
//		return err
//	}
//	defer w.Close()
//
//	// 尝试多种参数组合
//	configs := []*dsnetlzw.ReaderConfig{
//		{LSB: false, LitWidth: 8, EarlyChange: false}, // Unix compress 常见配置
//		{LSB: false, LitWidth: 9, EarlyChange: true},
//		{LSB: true, LitWidth: 8, EarlyChange: false},
//	}
//
//	for _, cfg := range configs {
//		r.Seek(0, 0) // 重置文件指针
//		lzwReader, err := lzw.NewReader(r, cfg)
//		if err != nil {
//			continue
//		}
//
//		_, err = io.Copy(w, lzwReader)
//		lzwReader.Close()
//		if err == nil {
//			return nil
//		}
//	}
//	return fmt.Errorf("所有参数组合尝试失败")
//}

func uncompressWithXz(src, dest string) error {
	r, err := os.Open(src)
	if err != nil {
		return err
	}
	defer r.Close()

	xzReader, err := xz.NewReader(r, 0)
	if err != nil {
		return err
	}

	outFile, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer outFile.Close()

	_, err = io.Copy(outFile, xzReader)
	return err
}

func getFileType(filename string) (string, error) {
	file, err := os.Open(filename)
	if err != nil {
		return "", err
	}
	defer file.Close()

	buf := make([]byte, 512) // 读取文件头512字节足够识别大多数格式
	_, err = file.Read(buf)
	if err != nil {
		return "", err
	}

	switch {
	case bytes.HasPrefix(buf, []byte{0xFD, '7', 'z', 'X', 'Z', 0x00}):
		return "xz", nil
	case bytes.HasPrefix(buf, []byte{0x1F, 0x8B}):
		return "gz", nil
	case bytes.HasPrefix(buf, []byte{'P', 'K', 0x03, 0x04}):
		return "zip", nil
	case bytes.HasPrefix(buf, []byte{0x1F, 0x9D}):
		return "Z (compress)", nil
	default:
		return "unknown", nil
	}
}
